/*
 * $LastChangedDate: 2007-06-22 11:50:57 +0200 (Fri, 22 Jun 2007) $
 * $LastChangedRevision: 212 $
 * $LastChangedBy: david $
 */

#include "FileMngr.h"
#include "SocketException.h"
#include "CouldNotOpenFileException.h"
#include "InvalidModeException.h"
#include <sstream>


int FileMngr::numFileMngr = 0;

FileMngr::FileMngr(std::string path, FileLock* lock, ServerSocket& socket) : Thread()
{
	this->fpath = path;
	this->socket = socket;
	this->lock = lock;
	id = "(FileMngr";
	std::stringstream ss;
	std::string str;
	ss << numFileMngr++;
	ss >> str;
	id.append(str);
	id.append(", " + fpath + ")");
	Log::println(*this, "creado FileMngr para el fichero " + fpath, Log::INIT);
}

FileMngr::~FileMngr()
{
	/* nop */
}

void FileMngr::open(std::string mode)
{
	if (mode == "r") {
		file.open(fpath.c_str(), std::ios::in);
	} else if (mode == "rw") {
		file.open(fpath.c_str(), std::ios::in | std::ios::out);
	} else {
		throw InvalidModeException();
	}
	if (!file.is_open()) {
		throw CouldNotOpenFileException();
	}
}

/* Lee un byte del fichero */
std::string FileMngr::read()
{
	char c;
	std::string output;

	lock->readLock(this);
	Thread::sleep(200);

	/* Leer del fichero */
	file >> c;
	output +=c;
	if (file.eof()) {
		output = "ERROR: ALCANZADO EOF";
	}

	lock->unlock(this);

	return output;
}

/* Escribe el string indicado al fichero */
void FileMngr::write(std::string s)
{
	lock->writeLock(this);
	Thread::sleep(200);

	/* Escribir sobre el fichero */
	file << s;

	lock->unlock(this);
}

/* Divide el string 'str' en tokens delimitados por alguno de los carácteres de
 * 'delimiters' y los devuelve en el vector 'tokens' */
static void tokenize(const string& str,
		vector<string>& tokens,
		const string& delimiters)
{
	// Encontrar el principio del primer token
	string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	// Encontrar el final del primer token
	string::size_type pos = str.find_first_of(delimiters, lastPos);

	while (string::npos != pos || string::npos != lastPos)
	{
		// Encontrado token 
		tokens.push_back(str.substr(lastPos, pos - lastPos));
		// Encontrar el principio del siguiente token
		lastPos = str.find_first_not_of(delimiters, pos);
		// Encontrar el final del siguiente token
		pos = str.find_first_of(delimiters, lastPos);
	}
}

/*
 * Sirve a la petición indicada y devuelve una respuesta correspondiente. El
 * formato de la petición en caso de una petición de lectura es:
 * 	READ:<SEEK>
 * donde <SEEK> es un número que indica el seek del cual se debe leer del
 * fichero. En caso de una escritura el formato es:
 * 	WRITE:<SEEK>:<DATA>
 * donde <SEEK> indica el seek al cual escribir en el fichero y <DATA> indica
 * los datos a escribir.
 */
std::string FileMngr::servePetition(std::string petition)
{
	std::string answer;

	Log::println(*this, "Recibido petición " + petition, Log::CONNECTION);
	if (petition == "CLOSE") {
		if (file.is_open()) {
			file.close();
		}
		answer = "ACK CLOSE";
	} else {
		vector<string> tokens;
		tokenize(petition, tokens, ":");
		std::stringstream ss(tokens[1]);
		int seek;
		ss >> seek;
		if (tokens[0] == "READ") {
			file.seekg(seek);
			answer = read();
		} else if (tokens[0] == "WRITE") {
			file.seekp(seek);
			write(tokens[2]);
			answer = "ESCRITO " + tokens[2];
		} else {
			answer = "ERROR: COMANDO DESCONOCIDO";
		}
	}
	return answer;
}

/* Actualiza el id del FileMngr añadiendo el modo. */
void FileMngr::update_id(std::string mode)
{
	string::size_type pos = id.find_first_of(")", 0);
	id.insert(pos, ", " + mode);
}

void FileMngr::run()
{
	std::string input;
	std::string output;
	try {
		std::string fmode;
		socket >> fmode;
		update_id(fmode);
		open(fmode);
		socket << "FICHERO ABIERTO";

		while (input != "CLOSE") {
			socket >> input;
			output = servePetition(input);
			socket << output;
		}
	} catch (SocketException & e) {
		Log::println(*this, e.description()); 
	} catch (InvalidModeException & e) {
		socket << "ERROR: MODO NO VALIDO";
		socket >> input;
		output = servePetition(input);
		socket << output;
	} catch (CouldNotOpenFileException & e) {
		socket << "ERROR: NO SE HA PODIDO ABRIR EL FICHERO";
		socket >> input;
		output = servePetition(input);
		socket << output;
	}
}
